package game.coord;

import game.Abstract2DMap;
import game.Point;

import java.util.ArrayList;
import java.util.List;

public class MapGenerator
{
    private static <TYPE extends Object> List<TYPE> generateList(int count, Class<TYPE> clazz)
    {
        List<TYPE> list = new ArrayList<TYPE>(count);
        while (list.size() < count)
        {
            try
            {
                list.add(clazz.newInstance());
            }
            catch (InstantiationException | IllegalAccessException e)
            {
                e.printStackTrace();
            }
        }
        return list;
    }
    
    private static <TYPE extends Object> TYPE getCell(Point size, List<TYPE> cells, Point position)
    {
        int index = Abstract2DMap.calculateIndex(position.getX(), position.getY(), size);
        return cells.get(index);
    }
    
    public static <TYPE extends MooreCell<TYPE>> List<TYPE> generateMooreMap(Point size, Class<TYPE> cellClass)
    {
        int sumCells = size.getSum();
        List<TYPE> list = generateList(sumCells, cellClass);
        rebuildMooreMap(size, list);
        return list;
    }
    
    public static <TYPE extends MooreCell<TYPE>> void rebuildMooreMap(Point size, List<TYPE> cells)
    {
        if (cells.size() != size.getSum())
        {
            throw new IllegalArgumentException("cells.size(" + cells.size() + ") != size.getSum(" + size.getSum() + ")");
        }
        for (int i = 0; i < cells.size(); i++)
        {
            TYPE cell = cells.get(i);
            Point position = Abstract2DMap.calculatePosition(i, size);
            // RIGHT:
            Point RIGHT = new Point(position.getX() + 1, position.getY());
            if (RIGHT.getX() >= size.getX())
            {
                RIGHT.setX(0);
            }
            cell.setRight(getCell(size, cells, RIGHT));
            // BOTTOM:
            Point BOTTOM = new Point(position.getX(), position.getY() + 1);
            if (BOTTOM.getY() >= size.getY())
            {
                BOTTOM.setY(0);
            }
            cell.setBottom(getCell(size, cells, BOTTOM));
            // LEFT:
            Point LEFT = new Point(position.getX() - 1, position.getY());
            if (LEFT.getX() < 0)
            {
                LEFT.setX(size.getX() - 1);
            }
            cell.setLeft(getCell(size, cells, LEFT));
            // TOP:
            Point TOP = new Point(position.getX(), position.getY() - 1);
            if (TOP.getY() < 0)
            {
                TOP.setY(size.getY() - 1);
            }
            cell.setTop(getCell(size, cells, TOP));
            // BOTTOMLEFT:
            Point BOTTOMLEFT = new Point(position.getX(), position.getY());
            BOTTOMLEFT.setX(LEFT.getX());
            BOTTOMLEFT.setY(BOTTOM.getY());
            cell.setBottomLeft(getCell(size, cells, BOTTOMLEFT));
            // BOTTOMRIGHT:
            Point BOTTOMRIGHT = new Point(position.getX(), position.getY());
            BOTTOMRIGHT.setX(RIGHT.getX());
            BOTTOMRIGHT.setY(BOTTOM.getY());
            cell.setBottomRight(getCell(size, cells, BOTTOMRIGHT));
            // TOPLEFT:
            Point TOPLEFT = new Point(position.getX(), position.getY());
            TOPLEFT.setX(LEFT.getX());
            TOPLEFT.setY(TOP.getY());
            cell.setTopLeft(getCell(size, cells, TOPLEFT));
            // TOPRIGHT:
            Point TOPRIGHT = new Point(position.getX(), position.getY());
            TOPRIGHT.setX(RIGHT.getX());
            TOPRIGHT.setY(TOP.getY());
            cell.setTopRight(getCell(size, cells, TOPRIGHT));
        }
    }
    
    public static <TYPE extends VonNeumannCell<TYPE>> List<TYPE> generateVonNeumannMap(Point size, Class<TYPE> cellClass)
    {
        int sumCells = size.getSum();
        List<TYPE> list = generateList(sumCells, cellClass);
        rebuildVonNeumannMap(size, list);
        return list;
    }
    
    public static <TYPE extends VonNeumannCell<TYPE>> void rebuildVonNeumannMap(Point size, List<TYPE> cells)
    {
        if (cells.size() != size.getSum())
        {
            throw new IllegalArgumentException("cells.size(" + cells.size() + ") != size.getSum(" + size.getSum() + ")");
        }
        for (int i = 0; i < cells.size(); i++)
        {
            TYPE cell = cells.get(i);
            Point position = Abstract2DMap.calculatePosition(i, size);
            // RIGHT:
            Point RIGHT = new Point(position.getX() + 1, position.getY());
            if (RIGHT.getX() >= size.getX())
            {
                RIGHT.setX(0);
            }
            cell.setRight(getCell(size, cells, RIGHT));
            // BOTTOM:
            Point BOTTOM = new Point(position.getX(), position.getY() + 1);
            if (BOTTOM.getY() >= size.getY())
            {
                BOTTOM.setY(0);
            }
            cell.setBottom(getCell(size, cells, BOTTOM));
            // LEFT:
            Point LEFT = new Point(position.getX() - 1, position.getY());
            if (LEFT.getX() < 0)
            {
                LEFT.setX(size.getX() - 1);
            }
            cell.setLeft(getCell(size, cells, LEFT));
            // TOP:
            Point TOP = new Point(position.getX(), position.getY() - 1);
            if (TOP.getY() < 0)
            {
                TOP.setY(size.getY() - 1);
            }
            cell.setTop(getCell(size, cells, TOP));
        }
    }
}
